/********************************************************************************
 *    Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH    *
 *                                                                              *
 *              This software is distributed under the terms of the             *
 *              GNU Lesser General Public Licence (LGPL) version 3,             *
 *                  copied verbatim in the file "LICENSE"                       *
 ********************************************************************************/
#include "HiTpcUnpackTask.h"
#include "HiTpcUnpackedData.h"
#include "HiDaqUnpackUserFunction.h"
#include "HiDaqFormat.h"
#include "HiDaqInfoData.h"

#include "FairRootManager.h"
#include "FairRunAna.h"
#include "FairRuntimeDb.h"

#include <TClonesArray.h>
// ---- Default constructor -------------------------------------------
HiTpcUnpackTask::HiTpcUnpackTask()
    : FairTask("HiTpcUnpackTask")
{
    LOG(debug) << "Default Constructor of HiTpcUnpackTask";
}
// ---- Constructor with parameters (Optional) ---------------------------
HiTpcUnpackTask::HiTpcUnpackTask(HiDaqUnpackTask* BasicUnpacker, Bool_t savetotree)
    : FairTask("HiTpcUnpackTask"),
    fBasicUnpacker(BasicUnpacker),
    fSaveToTree(savetotree)
{
    LOG(debug) << "Constructor with parameters of HiTpcUnpackTask";
}

// ---- Destructor ----------------------------------------------------
HiTpcUnpackTask::~HiTpcUnpackTask() { 
    LOG(debug) << "Destructor of HiTpcUnpackTask"; 
}

// ----  Initialisation  ----------------------------------------------
void HiTpcUnpackTask::SetParContainers()
{
    LOG(debug) << "SetParContainers of HiTpcUnpackTask";
    // Load all necessary parameter containers from the runtime data base
    /*
    FairRunAna* ana = FairRunAna::Instance();
    FairRuntimeDb* rtdb=ana->GetRuntimeDb();
    
    <HiDaqUnpackTaskDataMember> = (<ClassPointer>*)
      (rtdb->getContainer("<ContainerName>"));
    */
}

// ---- Init ----------------------------------------------------------
InitStatus HiTpcUnpackTask::Init()
{
    LOG(debug) << "Initilization of HiTpcUnpackTask";

    // Get a handle from the IO manager
    FairRootManager* ioman = FairRootManager::Instance();
    if(ioman == 0)
    {
        LOG(error) << "FairRootManager not instantiated!";
        return kERROR;
    }

    fInputFile = fBasicUnpacker->GetInputFileStream();

    fDaqInfoArray = static_cast<TClonesArray*>(ioman->GetObject("HiDaqInfoData"));
    if (!fDaqInfoArray) {
        LOG(warn) << "-W- HiTpcUnpackTask::Init: No Data array!";
        return kERROR;
    }

    // Create the TClonesArray for the output data and register
    fUnpackedArray = new TClonesArray("HiTpcUnpackedData");
    ioman->Register("HiTpcUnpackedData", "HiTpcUnpack", fUnpackedArray, kTRUE);

    // Do whatever else is needed at the initilization stage
    // Create histograms to be filled
    // initialize variables

    return kSUCCESS;
}

// ---- ReInit  -------------------------------------------------------
InitStatus HiTpcUnpackTask::ReInit()
{
    LOG(debug) << "Initilization of HiTpcUnpackTask";
    return kSUCCESS;
}

// ---- Exec ----------------------------------------------------------
void HiTpcUnpackTask::Exec(Option_t* /*option*/) { 
    LOG(debug) << "Exec of HiTpcUnpackTask"; 

    // Clear the output array
    fUnpackedArray->Clear();

    // ----循环读取fDaqInfoArray的数据并处理----------------------------------
    for(int i = 0; i < fDaqInfoArray->GetEntriesFast(); i++){
        HiDaqInfoData* DaqInfo = static_cast<HiDaqInfoData*>(fDaqInfoArray->At(i));
        if (!DaqInfo || DaqInfo->fNPxi == 0) continue;
        uint8_t PXI_id = DaqInfo->fPxiID;
        // ---- 判断是否属于该探测器 ------------------------------
        auto iter = PXI_TO_DETECTOR.find(PXI_id);
        if(iter != PXI_TO_DETECTOR.end() && iter->second == DET_TPC){
            fInputFile->seekg(DaqInfo->fFEEAddress, std::ios::beg);
            // ---- 解包数据净荷 ------------------------
            uint32_t Data_processed = 0;
            while(Data_processed < DaqInfo->fFEELength) {
                uint32_t ch_header = static_cast<uint32_t>(readLE(fInputFile, 4)); //读取通道头部信息
                Data_processed += 4;

                uint8_t channel_id = (ch_header >> 24) & 0xFF;       // 通道ID (8bit)
                uint16_t timeoffset = (ch_header >> 12) & 0xFFF;  // 时间窗口内偏移 (12bit)
                uint16_t data_point_num = ch_header & 0xFFF;       // 数据点个数 (12bit)
                // 计算需要读取几个32bit(4Byte) (每3个点打包成4Byte)
                uint32_t word_num = (data_point_num / 3) + (data_point_num % 3 > 0 ? 1 : 0); // 向上取整
                uint8_t flags = 0; //定义2bit标志位
                std::vector<uint16_t> cluster_data; //记录一个通道内的峰值数据

                for (uint32_t j = 0; j < word_num; j++) {
                    uint32_t data_word = static_cast<uint32_t>(readLE(fInputFile, 4));
                    Data_processed += 4;
                    // 提取标志位 (高2位)
                    flags = (data_word >> 30) & 0x3;
                    // 提取三个10位采样点，采样点顺序也要反过来
                    uint16_t point3 = (data_word >> 20) & 0x3FF;
                    uint16_t point2 = (data_word >> 10) & 0x3FF;
                    uint16_t point1 = data_word & 0x3FF;
                    // 排除最后一个4Byte 补零的数据
                    int points_in_word = 3;
                    if (j == word_num - 1) { // 最后一个4Byte
                        points_in_word = data_point_num % 3;
                        if (points_in_word == 0) points_in_word = 3;
                    }

                    // 添加到通道数据
                    if(points_in_word >= 1) cluster_data.emplace_back(point1);
                    if(points_in_word >= 2) cluster_data.emplace_back(point2);
                    if(points_in_word == 3) cluster_data.emplace_back(point3);  
                }
                // 创建 cluster 对象
                new ((*fUnpackedArray)[fUnpackedArray->GetEntriesFast()]) 
                HiTpcUnpackedData(DaqInfo->fTimeSliceID,
                                DaqInfo->fPxiID, 
                                DaqInfo->fFeeID, 
                                channel_id,
                                timeoffset,
                                cluster_data);
                if(flags == 0x2){// 10: 整个FEE的最后一个32bit数据
                    // std::cout<<"End of FEE data."<<std::endl;
                    break; // 跳出while循环，处理下一个FEE    
                }
            }            
        }
    }
}

// ---- Finish --------------------------------------------------------
void HiTpcUnpackTask::Finish() { 
    LOG(debug) << "Finish of HiTpcUnpackTask"; 
}

ClassImp(HiTpcUnpackTask);
